﻿#region Copyright
/* © 2008 George Tsiokos, licensed under the Microsoft Reciprocal License (Ms-RL) */
#endregion
using System.Threading;
using Comet.Net.Interop;
using System;

namespace Comet.ComponentModel {
    /// <summary>
    /// The bus holds a single value of type T once it has been published.
    /// </summary>
    /// <remarks>
    /// An in-memory implementation of the Publish-Subscribe pattern, 
    /// where only the latest value is retained. It's possible that a
    /// published value can be newer between the time a thread is signed
    /// and when it reads the value.
    /// </remarks>
    /// <typeparam name="T">Type to store on the bus</typeparam>
    public sealed class Bus<T> : IBus<T> {

        #region Fields (3)

        private volatile Semaphore _semaphore = CreateSemaphore ();
        private volatile ImmutableValueWrapper<T> _value;

        #endregion Fields

        #region Properties (1)

        /// <summary>
        /// Latest value published to the bus
        /// </summary>
        /// <exception cref="InvalidOperationException">No value has been published - you must publish a value first, before attempting to retrieve a value</exception>
        public T Value {
            get {
                try {
                    return _value.Value;
                }
                catch (NullReferenceException) {
                    throw new InvalidOperationException ("No value has been published");
                }
            }
        }

        #endregion Properties

        #region Methods (3)


        // Public Methods (2) 

        /// <summary>
        /// Publishes a value to the bus. All subscribers are notified immediately after the value is changed.
        /// </summary>
        /// <param name="value">the value to publish</param>
        public void Publish (T value) {
            _value = new ImmutableValueWrapper<T> (value);

            using (Semaphore flag = _semaphore) {
                _semaphore = CreateSemaphore ();
                flag.Release (ComponentModelUtility.SemaphoreCount);
            }
        }

        /// <summary>
        /// Returns a WaitHandle that is signaled when a value is published.
        /// </summary>
        /// <remarks>You must re-subscribe - once the WaitHandle is signaled, it is not reset</remarks>
        /// <returns></returns>
        public WaitHandle Subscribe () {
            return _semaphore;
        }



        // Private Methods (1) 

        private static Semaphore CreateSemaphore () {
            return new Semaphore (0, ComponentModelUtility.SemaphoreCount);
        }


        #endregion Methods

    }
}